/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

BuildGraph::BuildGraph(string metadir, string srcdir, string outdir)
	: m_metadir(metadir)
	, m_srcdir(srcdir)
	, m_outdir(outdir)
{
	
}

BuildGraph::~BuildGraph()
{
	Clear();
}

void BuildGraph::Clear()
{
	m_tests.clear();
	m_targets.clear();
	
	//Delete nodes last-to-first
	for(int i=m_nodes.size() - 1; i >= 0; i--)
		delete m_nodes[i];
	m_nodes.clear();
	
	//Delete module info
	for(auto it : m_rpcinfo)
		delete it.second;
	m_rpcinfo.clear();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Initialization

void BuildGraph::AddNode(BuildGraphNode* node)
{
	m_nodes.push_back(node);
}

void BuildGraph::AddNodeWithPath(BuildGraphNode* node, string path, string arch)
{
	auto p = pair<string, string>(path, arch);
	if(m_pathnodes.find(p) != m_pathnodes.end())
		FatalError("Another node already exists for path \"%s\" and architecture \"%s\"\n", path.c_str(), arch.c_str());
	
	m_nodes.push_back(node);
	m_pathnodes[p] = node;
}

//Adds an alternate path for a node already in the graph
void BuildGraph::AddAlternatePath(BuildGraphNode* node, std::string path, std::string arch)
{
	auto p = pair<string, string>(path, arch);
	if(m_pathnodes.find(p) != m_pathnodes.end())
		FatalError("Another node already exists for path \"%s\" and architecture \"%s\"\n", path.c_str(), arch.c_str());
	m_pathnodes[p] = node;
}

/**
	@brief Adds a node to the list of targets.
	
	This node must already be in the graph.
 */
void BuildGraph::AddTarget(BuildGraphNode* node, string name)
{
	if(m_targets.find(name) != m_targets.end())
		FatalError("Another node already exists for target name \"%s\"\n", name.c_str());
	
	m_targets[name] = node;
}

void BuildGraph::AddTest(BuildGraphNode* node)
{
	m_tests.push_back(node);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Accessors

BuildGraphNode* BuildGraph::GetNodeForPath(string path, string arch)
{
	//Try the requested architecture
	auto p = pair<string, string>(path, arch);
	if(m_pathnodes.find(p) != m_pathnodes.end())
		return m_pathnodes[p];
	
	//Try again, using "generic" architecture (for stuff like constgen)
	p.second = "generic";
	if(m_pathnodes.find(p) != m_pathnodes.end())
		return m_pathnodes[p];
	
	return NULL;
}

/**
	@brief Gets a test given the name.
	
	Requires a linear search for now. TODO: Decide if a map is a more appropriate data structure
 */
BuildGraphNode* BuildGraph::GetTest(string name)
{
	for(size_t i=0; i<m_tests.size(); i++)
	{
		UnitTestNode* t = dynamic_cast<UnitTestNode*>(m_tests[i]);
		if(!t)
			continue;
		if(t->GetName() == name)
			return t;
	}
	
	return NULL;
}

void BuildGraph::WriteModuleInfo(string buildroot)
{
	string modfname = buildroot + "/generic/modinfo.txt";
	FILE* fpInfo = fopen(modfname.c_str(), "w");
	if(!fpInfo)
		FatalError("Couldn't create modinfo.txt");
	for(auto it : m_rpcinfo)
		it.second->SaveToFile(fpInfo);
	fclose(fpInfo);
}
